#ifndef __SCHEDULER_H__
#define __SCHEDULER_H__

#include "fiber.h"
#include "thread.h"
#include <mutex>
#include <vector>

namespace lzp {

/**
 * @brief 协程调度器
 * @details 封装的是N-M的协程调度器
 * 内部有一个线程池，支持协程在线程池内切换 
 */
class Scheduler {
public:
     /**
      * @brief 创建调度器
      * @param[in] threads 线程数
      * @param[in] use_caller 是否将当前线程也作为调度线程
      * @param[in] name 名称
      */
    Scheduler(size_t threads = 1, bool use_caller = true, const std::string& name = "Scheduler");

    /**
     * @brief 析构函数
     */
    virtual ~Scheduler();

    /**
     * @brief 获取调度器名称
     */
    const std::string& getName() const { return m_name; }

public:
    /**
     * @brief 获取当前线程调度器指针
     */
    static Scheduler* GetThis();

     /**
      * @brief 获取当前线程的主协程
      */
    static Fiber* GetMainFiber();
protected:
    /**
     * @brief 设置当前的协程调度器
     */
    void SetThis();

public:
    /**
     * @brief 添加调度任务
     * @tparam FiberOrCb 调度任务类型，可以是协程对象或函数指针
     * @param[] fc 协程对象或指针
     * @param[] thread 指定运⾏该任务的线程号，-1表示任意线程
     */
    template<class FiberOrCb>
    void schedule(FiberOrCb fc, int thread = -1) {
        bool need_tickle = false;
        {
            std::lock_guard<std::mutex> lock(m_mutex);
            // empty ->  all thread is idle -> need to be waken up
            need_tickle = m_tasks.empty();

            ScheduleTask task(fc, thread);
            if (task.fiber || task.cb) {
                m_tasks.push_back(task);
            }
        }

        if (need_tickle) {
            tickle();
        }
    }

    /**
     * @brief 启动调度器
     */
    virtual void start();


    /**
     * @brief 停⽌调度器，等所有调度任务都执⾏完了再返回
     */
    virtual void stop();

protected:
    /**
     * @brief 通知协程调度器有任务了
     */
    virtual void tickle();

    /**
     * @brief 协程调度函数
     */
    virtual void run();

    /**
     * @brief ⽆任务调度时执⾏idle协程
     */
    virtual void idle();

    /**
     * @brief 返回是否可以停⽌
     */
    virtual bool stopping();

    /**
     * @brief 返回是否有空闲线程
     * @details 当调度协程进⼊idle时空闲线程数加1，从idle协程返回时空闲线程数减1
     */
    bool hasIdleThreads() { return m_idleThreadCount > 0; }

private:
    /**
     * @brief 调度任务，协程/函数⼆选⼀，可指定在哪个线程上调度
     */
    struct ScheduleTask {
        std::shared_ptr<Fiber> fiber;
        std::function<void()> cb;
        int thread; // 指定任务需要运行的线程id

        ScheduleTask() {
            fiber = nullptr;
            cb = nullptr;
            thread = -1;
        }

        ScheduleTask(std::shared_ptr<Fiber> f, int thr) {
            fiber = f;
            thread = thr;
        }

        ScheduleTask(std::shared_ptr<Fiber>* f, int thr) {
			fiber.swap(*f);
			thread = thr;
		}

        ScheduleTask(std::function<void()> f, int thr) {
			cb = f;
			thread = thr;
		}		

		ScheduleTask(std::function<void()>* f, int thr) {
			cb.swap(*f);
			thread = thr;
		}

        void reset() {
			fiber = nullptr;
			cb = nullptr;
			thread = -1;
		}	
    };

private:
    // 调度器名称
    std::string m_name;
    // 线程池
    std::vector<std::shared_ptr<Thread>> m_threads;
    // 线程id list
    std::vector<int> m_threadIds;
    // 互斥锁 -> 保护任务队列
    std::mutex m_mutex;
    // 任务队列
    std::vector<ScheduleTask> m_tasks;
    // 工作线程数量，不包含use_caller的主线程
    size_t m_threadCount = 0;
    // 活跃线程数
    std::atomic<size_t> m_activeThreadCount = {0};
    // 空闲线程数
    std::atomic<size_t> m_idleThreadCount = {0};

    // 主线程是否用作工作线程
    bool m_useCaller;
    // 如果是 -> 需要额外创建调度协程
    std::shared_ptr<Fiber> m_schedulerFiber;
    // 如果是 -> 调度器所在线程的id
    int m_rootThread = -1;
    // 是否正在关闭
    bool m_stopping = false;
};

}

#endif
